package com.skynet.springboot.mybatis.springmybatis.interceptor;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Skynet
 * @Date 2017/9/13-22:45.
 */
@Intercepts(
        {
                @Signature(type = StatementHandler.class, method = "parameterize", args = {Statement.class})
        }
)
public class AutoMapSQLInterceptor implements Interceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(AutoMapSQLInterceptor.class);

    private static StringBuilder fsql;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //执行目标方法
        Object result = invocation.proceed();
        try {
            LOGGER.info("**************打印SQL语句**************");
            StatementHandler sh = (StatementHandler) invocation.getTarget();
            setParameters(sh);
            //去除\n \s符号
            Pattern pattern = Pattern.compile("\\s+");
            Matcher matcher = pattern.matcher(fsql.toString());
            LOGGER.info(matcher.replaceAll(" "));
            LOGGER.info("*************************************");
        } catch (Exception e) {
            LOGGER.error("**********************执行拦截器发生异常**********************");
            LOGGER.error(e.getMessage(), e);
        }
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    private void setParameters(StatementHandler sh) throws Exception {
        BoundSql boundSql = sh.getBoundSql();
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        fsql = new StringBuilder(boundSql.getSql());
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
                    if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = SystemMetaObject.forObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    setParameter(boundSql, parameterMappings.size(), i, value);
                }
            }
        }
    }

    private void setParameter(BoundSql boundSql, int length, int index, Object value) {
        String sql = boundSql.getSql();
        String[] split = sql.split("\\?");
        fsql.append(split[index])
                .append("'")
                .append(value)
                .append("'");
        if (split.length > length ) {
            fsql.append(split[length]);
        }
    }
}
